#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include "util.hpp"
#include "log.hpp"

namespace ns_index
{
    /*
        建立索引：正排索引 和 倒排索引
    */
   struct DocInfo
   {
        std::string title;
        std::string content;
        std::string url;
        uint64_t doc_id;
   };

   struct InvertedElem
   {
        uint64_t doc_id;
        std::string word;
        int weight;
   };

   typedef std::vector<InvertedElem> InvertedList;
   

   class Index
   {
    private:
        // 1.正排索引，将Doc文档中放到vector容器中，容器和doc_id的下标一一对应
        std::vector<DocInfo> forward_index;
        
        // 2.倒排索引，关键词可能对用多个Doc文档
        std::unordered_map<std::string,InvertedList> inverted_index;

    private:
        Index(){}
        Index(const Index&) =delete;
        Index& operator=(const Index&)=delete;
        static Index* instance;
        static std::mutex lock;
    public:
        ~Index(){}

    public:
        static Index* getInstance()
        {
            if(instance == nullptr)
            {
                lock.lock();
                if(instance == nullptr)
                {
                    instance = new Index();
                }
                lock.unlock();
            }
            return instance;
        }

        // 1.根据doc_id获取Doc文档内容(正排索引)
        DocInfo* GetForwardDoc(uint64_t doc_id)
        {
            if(doc_id >= forward_index.size())
            {

                // std::cerr << "the doc_id find fail in forward_index" << std::endl;
                return nullptr;
            }
            return &forward_index[doc_id];
        }

        // 2.根据关键字来获取倒排拉链
        InvertedList *GetInvertedList(const std::string &word)
        {
            auto iter = inverted_index.find(word);
            if(iter == inverted_index.end()){
                std::cerr << word << " have no InvertedList" << std::endl;
                return nullptr;
            }
            return &(iter->second);
        }

        // 3.建立索引：正排索引，倒排索引
        bool BuildIndex(const std::string &input) //parse处理完毕的数据交给我
        {
            std::ifstream in(input, std::ios::in | std::ios::binary);
            if(!in.is_open()){
                std::cerr << "sorry, " << input << " open error" << std::endl;
                return false;
            }

            std::string line;
            int count = 0;
            while(std::getline(in, line)){
                DocInfo * doc = BuildFowardIndex(line);

                
                if(nullptr == doc){
                    std::cerr << "build " << line << " error" << std::endl; //for deubg
                    continue;
                }

                BuildInvertedIndex(*doc);
                count++;
                if(count % 50 == 0){
				
                    // std::cout <<"当前已经建立的索引文档: " << count <<std::endl;
                    LOG(NORMAL, "当前的已经建立的索引文档: " + std::to_string(count));
                }
            }
            return true;
        }

    private:
        DocInfo* BuildFowardIndex(const std::string& line)
        {
            // 1.line 的格式 title'\3'content'\3'url
            std::vector<std::string> result;
            std::string sep = "\3";
            ns_util::StringUtil::Split(line,&result,sep);

            if(result.size() != 3) return nullptr;

            // 2.到这说明解析split成功，存到result中有3个元素
            DocInfo doc;
            doc.title = result[0];
            doc.content = result[1];
            doc.url = result[2];
            doc.doc_id = forward_index.size();  // doc_id和正排索引数组下标绑定

            // 3.将doc保存到正排索引中
            forward_index.push_back(std::move(doc));

            return &forward_index.back();
        } 

        bool BuildInvertedIndex(const DocInfo &doc)
        {
            // 1.给定一个doc，建立倒排索引
            // 2.建立倒排拉链 [1]->hello world
            struct world_count
            {
                int title_cnt = 0;
                int content_cnt = 0;
            };
            std::unordered_map<std::string,world_count> cnt_map;

            // 3.对title分词
            std::vector<std::string> title_worlds;
            ns_util::JiebaUtil::CutString(doc.title,&title_worlds);

            for(auto word : title_worlds)
            {
                boost::to_lower(word);
                cnt_map[word].title_cnt++;
            }

            // 4.对content分词
            std::vector<std::string> content_worlds;
            ns_util::JiebaUtil::CutString(doc.content,&content_worlds);

            for(auto word : content_worlds)
            {
                boost::to_lower(word);
                cnt_map[word].content_cnt++;
            }

            // 5.相关性？？？？
            #define X 10
            #define Y 1
            // 
            for(auto &word_pair : cnt_map){
                InvertedElem item;
                item.doc_id = doc.doc_id;
                item.word = word_pair.first;
                item.weight = X * word_pair.second.title_cnt +  Y * word_pair.second.content_cnt; //相关性

                // inverted_index[word.first].push_back(elem);
                // 建立倒排索引，inverted_index倒排的序列。inverted_index[word_pair.first] 存在不添加，不存在添加 
                // 关键词 ：文档1，文档2......,哈希表（拉链法）
                // ---------------
                // |   |   | 
                // |
                InvertedList &inverted_list = inverted_index[word_pair.first]; 
                inverted_list.push_back(std::move(item));
            }

            return true;
        }
   };

   Index* Index::instance = nullptr;
   std::mutex Index::lock;

}
